'# Copyright (c) 2008-2011 Wesley Werner
'# Source code distributed under the BSD license
Public Class GraphProgressBar


    ' keep an array of the cell values. Keep in mind arrays are zero based.
    Private correctValues(_resolution - 1) As Long
    Private wrongValues(_resolution - 1) As Long

    ' keep an array of the cells that are active, ie that have been accessed using the Setvalue() method
    Private activeCells(_resolution - 1) As Boolean

    ' variable that stored the width of a cell. The height is determined by the control height.
    Private cellWidth As Integer = 1


#Region " properties "


    ' define the cell resolution. Keep in mind arrays are zero based.
    Private _resolution As Integer = 100
    ''' <summary>
    ''' define the cell resolution. Setting this value resets the progress graph.
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Property Resolution() As Integer
        Get
            Return _resolution
        End Get
        Set(ByVal value As Integer)
            _resolution = value
            Me.Reset()
        End Set
    End Property


    Private _BadColor As Color = Color.Black
    ''' <summary>
    ''' Color that represents a bad read
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Property BadColor() As Color
        Get
            Return _BadColor
        End Get
        Set(ByVal value As Color)
            _BadColor = value
        End Set
    End Property



    Private _GoodColor As Color = Color.LightGreen
    ''' <summary>
    ''' Color that represents a good read
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Property GoodColor() As Color
        Get
            Return _GoodColor
        End Get
        Set(ByVal value As Color)
            _GoodColor = value
        End Set
    End Property


    Private _Filesize As Long = 100
    ''' <summary>
    ''' Get or Set the filesize
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Property Filesize() As Long
        Get
            Return _Filesize
        End Get
        Set(ByVal value As Long)
            _Filesize = value
        End Set
    End Property



#End Region


    ''' <summary>
    ''' resets the graph cell values
    ''' </summary>
    ''' <remarks></remarks>
    Public Sub Reset()

        ReDim correctValues(_resolution - 1)
        ReDim wrongValues(_resolution - 1)
        ReDim activeCells(_resolution - 1)

        Array.Clear(correctValues, 0, _resolution - 1)
        Array.Clear(wrongValues, 0, _resolution - 1)
        Array.Clear(activeCells, 0, _resolution - 1)

        Me.Invalidate()

    End Sub


    ''' <summary>
    ''' set the value at the specified file position
    ''' </summary>
    ''' <param name="filePosition"></param>
    ''' <param name="hasErrors"></param>
    ''' <remarks></remarks>
    Public Sub Setvalue(ByVal filePosition As Long, ByVal hasErrors As Boolean)

        ' calculate the cell index using: (filePosition / Filesize) / resolution
        Static lastCellIndex As Integer = 0

        Try

            ' calculate the cell index for this position. -1 since the array is zero based
            Dim cellIndex As Integer = CInt((filePosition / _Filesize) * _resolution)

            If (cellIndex > activeCells.Length - 1) Then cellIndex = activeCells.Length - 1

            ' toggle this cell as active
            'activeCells(cellIndex) = True

            ' made any skipped cells, since last Set, also active
            For skippedIndice As Integer = lastCellIndex To cellIndex
                activeCells(skippedIndice) = True
                If hasErrors Then
                    ' increase the value
                    wrongValues(skippedIndice) += 1
                Else
                    ' increase the value
                    correctValues(skippedIndice) += 1
                End If
            Next

            lastCellIndex = cellIndex

            ' invalidate the cell so that it redraws
            Me.Invalidate()

        Catch ex As Exception
            'Stop
        End Try

    End Sub



    ''' <summary>
    ''' Render the specified cell using the graphics object
    ''' </summary>
    ''' <param name="cellIndex"></param>
    ''' <param name="g"></param>
    ''' <remarks></remarks>
    Private Sub DrawCell(ByVal cellIndex As Integer, ByVal g As Graphics)

        Try

            ' calculate the cell x offset using: cellIndex * cellWidth
            Dim offset As Integer = cellIndex * cellWidth


            ' inactive cells default to white
            If activeCells(cellIndex) Then

                ' get the total amount of reads, good and bad
                Dim total As Double = (wrongValues(cellIndex)) + correctValues(cellIndex)

                ' get the percentage of bad reads
                Dim perc As Double = (wrongValues(cellIndex) / total) * 100

                ' calculate the height of the bad rectangle, a percentage of the control height
                Dim badHeight As Integer = CInt((Me.Height / 100) * perc)

                ' calculate the height of the good rectangle
                Dim goodHeight As Integer = (Me.Height - badHeight)

                ' render the block in the good color
                g.FillRectangle(New SolidBrush(Me.GoodColor), offset, 0, cellWidth, goodHeight)

                ' render the bad color block, its height is relative to the percentage of bad vs good values for this cell index
                g.FillRectangle(New SolidBrush(Me.BadColor), offset, goodHeight, cellWidth, badHeight)

            End If

            Dim bounds As Rectangle

            ' render a border
            bounds = New Rectangle(0, 0, Me.Width, Me.Height)
            ControlPaint.DrawBorder(g, bounds, Color.Silver, ButtonBorderStyle.Inset)

        Catch ex As Exception
            Application.DoEvents()
        End Try

    End Sub



    ''' <summary>
    ''' constructor
    ''' </summary>
    ''' <remarks></remarks>
    Public Sub New()

        ' This call is required by the Windows Form Designer.
        InitializeComponent()

        ' set the control styles
        Me.SetStyle(ControlStyles.AllPaintingInWmPaint, True)
        Me.SetStyle(ControlStyles.UserPaint, True)
        Me.SetStyle(ControlStyles.OptimizedDoubleBuffer, True)

        Reset()

    End Sub



    ''' <summary>
    ''' control resize must calculate variable values
    ''' </summary>
    ''' <param name="sender"></param>
    ''' <param name="e"></param>
    ''' <remarks></remarks>
    Private Sub GraphProgressBar_Resize(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Resize

        Try

            ' calc the cell width
            cellWidth = CInt(Math.Ceiling(Me.Width / _resolution))

            ' invalidate the control so that the cells redraw
            Me.Invalidate()

        Catch ex As Exception

        End Try

    End Sub


    ''' <summary>
    ''' override the OnPaint event
    ''' </summary>
    ''' <param name="e"></param>
    ''' <remarks></remarks>
    Protected Overrides Sub OnPaint(ByVal e As System.Windows.Forms.PaintEventArgs)
        MyBase.OnPaint(e)
        DrawCells(e.Graphics)
    End Sub


    ''' <summary>
    ''' draw all cells on a graphics object
    ''' </summary>
    ''' <param name="g"></param>
    ''' <remarks></remarks>
    Private Sub DrawCells(ByVal g As Graphics)

        ' draw all the cells
        For index As Integer = 0 To _resolution - 1
            DrawCell(index, g)
        Next

        ' render a glass effect on half of the bar
        Dim Bounds As New Rectangle(0, 0, Me.Width, CInt(Me.Height / 3))
        g.FillRectangle(New SolidBrush(Color.FromArgb(64, 255, 255, 255)), Bounds)

    End Sub


    ''' <summary>
    ''' get the current progress graph as an image
    ''' </summary>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Friend Function GetGraphImage() As Image
        Dim img As New Bitmap(Me.ClientSize.Width, Me.ClientSize.Height)
        Dim g As Graphics = Graphics.FromImage(img)
        DrawCells(g)
        Return img
    End Function


End Class
